/* 
 * tigerrad
 * fall 2006
 */

#ifndef _ENGINE_H
#define _ENGINE_H

#include "Util.h"
#include "Scene.h"
#include "Program.h"
#include "Framebuffer.h"
#include "Camera.h"
#include "Light.h"
#include "FloatTexture.h"
#include "shared.h"

#define DIRECT_ONLY 0
#define RADIOSITY 1
#define INDIRECT_ONLY 2
#define NEGATIVE_ONLY 3

/**
 * 3D renderer using OpenGL.
 */
class Engine
{
	public:
		/**
		 * Constructor.
		 */
		Engine();

		/**
		 * Init OpenGL and engine state.
		 * @param size Init window size.
		 */
		void init(const Vec2s& size);

		/**
		 * Handle window resize.
		 * @param size Window size 
		 */
		void resize(const Vec2s& size);
		
		/**
		 * Set the current Scene.
		 */
		void setScene(ScenePtr s) { _scene = s; }

		/**
		 * Render the current scene.
		 */
		void render();

		/**
		 * Set the number of secondary lights.
		 */
		void setNumSecLights(GLuint num) { _numSecLights = num; }
		
		/**
		 * Return the number of secondary lights.
		 */
		GLuint getNumSecLights() { return _numSecLights; }

		/**
		 * Toggle through radiosity, direct, and indirect
		 */
		void toggleRadiosity() { _radOn++; if (_radOn>3) _radOn=0; }

		/**
		 * Toggle determining light positions from the camera (or from the light).
		 */
		void toggleSecLightsFromCamera() { _secLightsFromCamera = ! _secLightsFromCamera; }

		/**
		 * Toggle displaying the view from the light.
		 */
		void toggleShowFromLight() { _showFromLight = !_showFromLight; }

		/**
		 * Which texture to display when rendering off screen textures
		 */
		void displayTex(int i) { _displayTex = i; }

		/**
		 * Toggle display the secondary light locations.
		 */
		void toggleShowSecLights() { _showSecLights = !_showSecLights; }

		/**
		 * Show the deferred shader output.
		 */
		void toggleShowDS() { _showDS = !_showDS; }

		/**
		 * Compute the samples used in selecting secondary lights.
		 * @param hemi Use hemisphere samples if true.
		 */
		void computeSamples(bool hemi=true);
	
		/**
		 * Toggle the use of hemisphere sampling on/off.
		 */
		void toggleHemisphereSampling() { _useHemisphereSampling = !_useHemisphereSampling; }

		void toggleNegativeIndirectLight() { _useNegativeIndirectLight = !_useNegativeIndirectLight; }
	private:
		/**
		 * TODO wording...
		 * Sample the secondary light positions using standard halton.
		 */
		void standardSamples();

		/**
		 * Populate the secondary light positions using hemispherical halton...or something like that...
		 */
		void hemisphereSamples(const Camera& lightCam);
	
		/**
		 * Render the scene from the light into a RenderTarget.
		 * @param l Light to render from
		 * @param lc Camera at light
		 * @param c The main camera from which the scene will be viewed from.
		 */
		void renderFromLight(LightPtr l, const Camera& lc, const Camera& c);

		/**
		 * Render deferred shader textures
		 * @param l Light in scene
		 * @param lc Camera at light
		 * @param c Main camera
		 */
		void renderDS(LightPtr l, const Camera& lc, const Camera& c);

		/**
		 * Sort the indirect lights by distance from the camera
		 */
		void sortIndirect(const Camera& cam, GLfloat*, GLfloat*, GLfloat*);

		/**
		 * Render indirect into indirect texture.
		 */
		int renderIndirect(const Camera& cam, TexturePtr ds[3], 
                           TexturePtr indirect[2][3], 
                           int offset, int pingpong, GLfloat prevScale, GLfloat scale);
		
		int renderHiIndirect(const Camera& cam, TexturePtr ds[3], 
                             TexturePtr indirect[2][3], 
                             int offset, int pingpong, GLfloat prevScale);

		int renderNegIndirect(const Camera& cam, TexturePtr ds[3], 
                              TexturePtr negIndirect[2], 
                              TexturePtr indirectLights[3], 
                              int offset, int pingpong, GLfloat prevScale, GLfloat scale);
		
		int renderHiNegIndirect(const Camera& cam, TexturePtr ds[3], 
                              TexturePtr negIndirect[2], 
                                TexturePtr indirectLights[3], 
                                int offset, int pingpong, GLfloat prevScale, GLfloat scale);
		/**
		 * Render the final image
		 */
		void renderFinal(const Camera& cam);

		/**
		 * Render secondary light textures
		 */
		void renderSecLights();

		/**
		 * Render each Entity instance in the Scene using the light 
		 * and from the camera.
		 */
		void drawEntities(LightPtr l, const Camera& c);

		/**
		 * Draw the model
		 */
		void drawModel(ModelPtr m);

		/**
		 * Draw the light
		 */
		void drawLight(LightPtr l, const Camera& c);

		/**
		 * Draw a quad.
		 */
		void drawQuad(int x, int y, int width, int height);

		/**
		 * Da Scene...
		 */
		ScenePtr _scene;

		/**
		 * Down sample program.
		 */
		ProgramPtr _avgDownProg;
		/**
		 * Deferred shader programs.
		 */
		ProgramPtr _dsProg; 

		/**
		 * Deferred shader textures.
		 */
		TexturePtr _ds[LO_RES_INDEX+1][3];

		/**
		 * Project halton points
		 */
		TexturePtr _indirectLightSamples;

		/** 
		 * Indirect light textures.
		 */
		TexturePtr _indirectLights[3];

		/**
		 * Framebuffer object
		 */
		FramebufferPtr _fboIndirectLights;

		/**
		 * Gather the indirect lights
		 */
		ProgramPtr _gatherIndirectProg; 

		/**
		 * Framebuffers used to render to textures.
		 */
		FramebufferPtr _fboFromLight;
		FramebufferPtr _fboDS[LO_RES_INDEX+1];
		FramebufferPtr _fboIndirect;
		FramebufferPtr _fboNegIndirect;
		FramebufferPtr _fboUpSample;
		FramebufferPtr _fboDis;

		/**
		 * Shaders to render from the light
		 */
		ProgramPtr _fromLightProg; 

		/**
		 * Render textures from the light.
		 * 0 is ec
		 * 1 is normal
		 * 2 is color
		 * 4 is depth 
		 */
		TexturePtr _fromLight[4];
		
		/**
		 * Used in storing the indirect light
		 */
		//TexturePtr _indirect[4][2];
        // RAC: neg light test
		TexturePtr _indirect[LO_RES_INDEX+1][2][3];
		TexturePtr _negIndirect[LO_RES_INDEX+1][2];

		/**
		 * The default GLSL program.
		 */
		ProgramPtr _defaultProgram;

		/**
		 * Secondary light and neg secondary light GLSL program.
		 */
		ProgramPtr _secLightProgram;
		ProgramPtr _negLightProgram;

		/**
		 * GLSL Program to visualize light positions
		 */
		ProgramPtr _showSecLightsProgram;

		/**
		 * Program to generate the discontinuity buffer
		 */
		ProgramPtr _disProgram;
		
		/**
		 * Program to downsample the discontinuity buffer
		 */
		ProgramPtr _dsDisProgram;

		/** 
		 * Discontinuity buffers
		 */
		TexturePtr _disBuffer[DIS_INDEX+1];

		/**
		 * Window width,height
		 */
		Vec2s _size;	

		/**
		 * Number of secondary lights
		 */
		GLuint _numSecLights;
		GLuint _numNegLights;

		/**
		 * Indirect light indexes into textures
		 */
		std::vector<Vec2s> _indirectSamples;
        Vec2f* _secLightIndexes;

		/**
		 * Radiosity is on.
		 */
		unsigned int _radOn;

		/**
		 * Show the diffuse view from the light
		 */
		bool _showFromLight;

		int _displayTex;
		bool _showSecLights;
		bool _useNegativeIndirectLight;
		bool _showDS;
		bool _secLightsFromCamera;
		bool _useHemisphereSampling;
        //corners of screenspace bounding box for neg point lights
        Vec2f _BBmax;
        Vec2f _BBmin;
};

typedef boost::shared_ptr<Engine> EnginePtr;

#endif

